#pragma once

#include <atomic>
#include <memory>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>

#define DG_UNUSED(x) (void)x

#define CHECK_WP(wp)     \
    auto me = wp.lock(); \
    if (me == nullptr) { \
        return;          \
    }

#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif  // MAX

#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif  // MIN

template <typename T, typename S>
static inline std::shared_ptr<T> trans_type(std::shared_ptr<S> obj) {
    return std::dynamic_pointer_cast<T>(obj);
}

template <typename T, typename S>
static inline bool is_type(std::shared_ptr<S> obj) {
    return trans_type<T>(obj) != nullptr;
}

// 禁止拷贝基类
class noncopyable {
protected:
    noncopyable() {}
    ~noncopyable() {}

private:
    // 禁止拷贝
    noncopyable(const noncopyable& that) = delete;
    noncopyable(noncopyable&& that) = delete;
    noncopyable& operator=(const noncopyable& that) = delete;
    noncopyable& operator=(noncopyable&& that) = delete;
};

// 可以保存任意的对象
class Any {
public:
    using Ptr = std::shared_ptr<Any>;

    Any() = default;
    ~Any() = default;

    template <typename C, typename... ArgsType>
    void set(ArgsType&&... args) {
        _data.reset(new C(std::forward<ArgsType>(args)...),
                    [](void* ptr) { delete (C*)ptr; });
    }
    template <typename C>
    C& get() {
        if (!_data) {
            throw std::invalid_argument("Any is empty");
        }
        C* ptr = (C*)_data.get();
        return *ptr;
    }

    operator bool() { return _data.operator bool(); }
    bool empty() { return !bool(); }

private:
    std::shared_ptr<void> _data;
};

// 用于保存一些外加属性
class AnyStorage : public std::unordered_map<std::string, Any> {
public:
    AnyStorage() = default;
    ~AnyStorage() = default;
    using Ptr = std::shared_ptr<AnyStorage>;
};

template <class C>
class ObjectStatistic {
public:
    ObjectStatistic() { ++getCounter(); }

    ~ObjectStatistic() { --getCounter(); }

    static size_t count() { return getCounter().load(); }

private:
    static std::atomic<size_t>& getCounter();
};

#define INSTANCE_IMP(class_name, ...)                    \
    class_name& class_name::Instance() {                 \
        static std::shared_ptr<class_name> s_instance(   \
            new class_name(__VA_ARGS__));                \
        static class_name& s_instance_ref = *s_instance; \
        return s_instance_ref;                           \
    }

#define StatisticImp(Type)                                     \
    template <>                                                \
    std::atomic<size_t>& ObjectStatistic<Type>::getCounter() { \
        static std::atomic<size_t> instance(0);                \
        return instance;                                       \
    }

#define StrPrinter util::_StrPrinter()
class _StrPrinter : public std::string {
public:
    _StrPrinter() {}

    template <typename T>
    _StrPrinter& operator<<(T&& data) {
        _stream << std::forward<T>(data);
        this->std::string::operator=(_stream.str());
        return *this;
    }

    std::string operator<<(std::ostream& (*f)(std::ostream&)) const {
        DG_UNUSED(f);
        return *this;
    }

private:
    std::stringstream _stream;
};

std::string hexdump(const void* buf, size_t len);
std::string exePath(bool isExe = true);
std::string exeDir(bool isExe = true);
std::string exeName(bool isExe = true);

std::vector<std::string> split(const std::string& s, const char* delim);

/**
 * @brief 字符串是否以xx开头
 *
 * @param str 源字符串
 * @param substr 目标字符串
 * @return true
 * @return false
 */
bool start_with(const std::string& str, const std::string& substr);

/**
 * @brief 字符串是否以xx结尾
 *
 * @param str 源字符串
 * @param substr 目标字符串
 * @return true
 * @return false
 */
bool end_with(const std::string& str, const std::string& substr);

/**
 * @brief 获取时间字符串
 *
 * @param fmt 时间格式，譬如%Y-%m-%d %H:%M:%S
 * @param time
 * @return std::string 时间字符串
 */
std::string getTimeStr(const char* fmt, time_t time = 0);

template <typename... Args>
std::string stringFormat(const std::string& format, Args... args) {
    const size_t size =
        1 +
        snprintf(nullptr, 0, format.c_str(), args...);  // Extra space for \0
    char bytes[size];
    snprintf(bytes, size, format.c_str(), args...);
    return std::string(bytes);
}

/**
 * @brief 根据unix时间戳获取本地时间
 *
 * @param sec unix时间戳
 * @return struct tm
 */
struct tm getLocalTime(time_t sec);

/**
 * @brief 获取时间差
 *
 * @param force
 * @return long 单位为秒
 */
long getGMTOff(bool force = false);

/**
 * 获取1970年至今的毫秒数
 * @param system_time
 * 是否为系统时间(系统时间可以回退),否则为程序启动时间(不可回退)
 */
uint64_t getCurrentMillisecond(bool system_time = false);

/**
 * 获取1970年至今的微秒数
 * @param system_time
 * 是否为系统时间(系统时间可以回退),否则为程序启动时间(不可回退)
 */
uint64_t getCurrentMicrosecond(bool system_time = false);

/**
 * @brief 设置线程名称
 *
 * @param name
 */
void setThreadName(const char* name);

/**
 * @brief 获取线程名称
 *
 * @return std::string
 */
std::string getThreadName();

/**
 * 设置当前线程cpu亲和性
 * @param i cpu索引，如果为-1，那么取消cpu亲和性
 * @return 是否成功，目前只支持linux
 */
bool setThreadAffinity(int i);

/**
 * 根据typeid(class).name()获取类名
 */
std::string demangle(const char* mangled);

#if ENABLE_UUID
/**
 * @brief generate uuid string
 *
 * @return std::string
 */
std::string genUUID();
#endif